package com.open.capacity.notice.statemachine;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;
import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;

import com.open.capacity.notice.enums.SendJobStatusChangeEnum;
import com.open.capacity.notice.enums.SendMessageJobStatus;
import com.open.capacity.notice.statemachine.action.JobActionFactory;
import com.open.capacity.notice.statemachine.action.JobStateAction;

/**
 * 状态机工厂
 */
@Component
public class StateMachineFactory {

	private final UntypedStateMachineBuilder jobStateMachineBuilder;

	@Resource
	private JobActionFactory jobActionFactory;

	// 初始化的逻辑
	private StateMachineFactory() {
		this.jobStateMachineBuilder = StateMachineBuilderFactory.create(JobStateMachine.class);

		// SendJobStatusChangeEnum包含了任务状态流转所有的事件，每个事件都会导致任务状态流转
		for (SendJobStatusChangeEnum event : SendJobStatusChangeEnum.values()) {
			// 如果说发生了Job_created事件，会导致状态流转，from null状态流转到created状态
			// 状态机，他的状态流转，是由一个一个事件去触发的，触发任务这里一个一个事件的时候，会导致任务状态机里的状态会发生一次流转
			// 发生流转了之后，call method，调用一个什么方法，状态机里自定义的一个方法，通过自定义方法去处理状态流转
			this.jobStateMachineBuilder.externalTransition().from(event.getFromStatus()).to(event.getToStatus())
					.on(event).callMethod("onStateChange");
		}

	}

	/**
	 * 获取任务状态机
	 * 
	 * @param initState 初始状态
	 * @return 状态机
	 */
	public JobStateMachine getJobStateMachine(SendMessageJobStatus initState) {
		StateMachineFactory.JobStateMachine JobStateMachine = this.jobStateMachineBuilder
				.newUntypedStateMachine(initState);
		JobStateMachine.setJobActionFactory(jobActionFactory);
		return JobStateMachine;
	}

	/**
	 * 状态机父类 先继承自我们自定义的基类，里面包含一些基础性的通用型的功能和方法 让他继续继承自squirrel框架提供的基类
	 */
	public static abstract class BaseStateMachine<S, E> extends AbstractUntypedStateMachine {
		private final ThreadLocal<Exception> exceptionThreadLocal = new ThreadLocal<>();

		/**
		 * 状态流传
		 */
		public void onStateChange(S fromStatus, S toState, E event, Object context) {
			try {
				// 状态机已经触发了一个事件，触发了以后，导致状态，state，从from里转到to去
				// 发生了什么事件，event，from，to，传递进来的任务创建request对象会作为context传递进来
				onStateChangeInternal(fromStatus, toState, event, context);
			} catch (Exception e) {
				exceptionThreadLocal.set(e);
			}
		}

		/**
		 * 正常情况下状态机：调用fire(event,context) 方法，会调用onStateChange方法。
		 * <p>
		 */
		@Override
		public void fire(Object event, Object context) {
			super.fire(event, context);
			Exception exception = exceptionThreadLocal.get();
			if (exception != null) {
				exceptionThreadLocal.remove();
				throw new RuntimeException(exception);
			}
		}

		/**
		 * 状态机装填流转核心逻辑
		 */
		protected abstract void onStateChangeInternal(S fromStatus, S toState, E event, Object context);

	}

	/**
	 * 任务状态机 这个注解，@StateMachineParameters，是squirrel框架提供的，描述任务状态机的
	 * contextType，在状态流转的时候，必然会有一个上下文的概念流转的时候传递数据，上下文提供数据 自定义状态机必须继承squirrel框架的基类
	 *
	 */
	@StateMachineParameters(stateType = SendMessageJobStatus.class, eventType = SendJobStatusChangeEnum.class, contextType = Object.class)
	public static class JobStateMachine extends BaseStateMachine<SendMessageJobStatus, SendJobStatusChangeEnum> {

		// 一看是什么东西，状态在流转的时候，流转到不同的状态的时候，就需要不同的action来触发
		// 比如说流转到created状态，created action来执行你的状态动作，生单业务流程编排
		private JobActionFactory JobActionFactory;

		public void setJobActionFactory(JobActionFactory JobActionFactory) {
			this.JobActionFactory = JobActionFactory;
		}

		@Override
		public void onStateChange(SendMessageJobStatus fromStatus, SendMessageJobStatus toState,
				SendJobStatusChangeEnum event, Object context) {
			super.onStateChange(fromStatus, toState, event, context);
		}

		@Override
		public void onStateChangeInternal(SendMessageJobStatus fromStatus, SendMessageJobStatus toState,
				SendJobStatusChangeEnum event, Object context) {
			// 真正去触发我们的任务创建事件，状态流转，处理动作的时候，会到这儿来
			// 触发了状态流转之后，会找到一个动作，action，action的话是从JobAtionFactory里获取出来的
			JobStateAction<?> action = JobActionFactory.getAction(event);
			if (action != null) {
				action.onStateChange(event, context);
			}
		}
	}

}
